package com.example.demo.core.filter;

import com.alibaba.fastjson.JSON;
import com.example.demo.core.annotation.Log;
import com.example.demo.core.utils.AnnotationUtils;
import com.example.demo.core.utils.HttpUtils;
import com.example.demo.core.result.CommonResult;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author kin
 * @since 2017/12/1
 */
@Slf4j
public class HttpLoggingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            LocalDateTime startTime = LocalDateTime.now();
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

            boolean hasAnnotation = AnnotationUtils.hasAnnotation("com.example.demo.controller", new AnnotationUtils.AnnotationModel(httpServletRequest.getRequestURI(), Log.class));

            Map<String, String> requestMap = this.getTypeSafeRequestMap(httpServletRequest);
            BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(
                    httpServletRequest);
            BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(
                    httpServletResponse);

            final StringBuilder logMessage = new StringBuilder(
                    "REST Request - ")
                    .append("[HTTP METHOD:")
                    .append(httpServletRequest.getMethod())
                    .append("] [PATH INFO:")
                    .append(httpServletRequest.getServletPath())
                    .append("] [REQUEST PARAMETERS:").append(requestMap)
                    .append("] [REQUEST BODY:")
                    .append(bufferedRequest.getRequestBody())
                    .append("] [REMOTE ADDRESS:")
                    .append(HttpUtils.getIpAddress(httpServletRequest)).append("]");

            filterChain.doFilter(bufferedRequest, bufferedResponse);

            LocalDateTime endTime = LocalDateTime.now();

            CommonResult commonResult = JSON.parseObject(bufferedResponse.getContent(), CommonResult.class);

            logMessage.append("[CODE:")
                    .append(commonResult.getCode())
                    .append("] [MESSAGE:")
                    .append(commonResult.getMessage())
                    .append("] [RESPONSE:")
                    .append(hasAnnotation ? commonResult.getData() : "")
                    .append("] [START:")
                    .append(startTime.format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")))
                    .append("] [END:")
                    .append(endTime.format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")))
                    .append("] [DURATION:")
                    .append(Duration.between(startTime, endTime).toMillis())
                    .append("ms]");
            logMessage.append("\n-----------------------------------------------------------------------------");

            log.info(logMessage.toString());
        } catch (Exception ex) {
            log.error(ex.toString());
        }

    }

    @Override
    public void destroy() {

    }

    private Map<String, String> getTypeSafeRequestMap(HttpServletRequest request) {
        Map<String, String> typeSafeRequestMap = new HashMap<>();
        Enumeration<?> requestParamNames = request.getParameterNames();
        while (requestParamNames.hasMoreElements()) {
            String requestParamName = (String) requestParamNames.nextElement();
            String requestParamValue = request.getParameter(requestParamName);
            typeSafeRequestMap.put(requestParamName, requestParamValue);
        }
        return typeSafeRequestMap;
    }

}
